#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include "json/json.h"
#include "waHttpClient.h"

#include "Log.h"
#include "wxapi.h"

int WeixinApi :: GetAccessToken( const string & sAppid, const string & sAppSeceret, string & sAccessTokenOut, int & iExpireTimeOut )
{	
	int ret = -1;
	char url[2048]={0};
	snprintf( url, sizeof(url), "http://api.weixin.qq.com/token.json?appid=%s&grant_type=client_credential&secret="
		"%s", sAppid.c_str(), sAppSeceret.c_str() );
	
	webapp::HttpClient httpClient( url );
	
	if ( 0 == httpClient.Execute() && httpClient.Status() == 200 )
	{
		LogDebug( httpClient.GetContent().c_str() );
		
		{
			Json::Value root;
			Json::Reader reader;
		
			if ( reader.parse( httpClient.GetContent(), root ) )
			{
				if ( root.isMember("access_token") && root.isMember("expires_in") )	
				{
					sAccessTokenOut = root["access_token"].asCString();
					iExpireTimeOut = root["expires_in"].asInt();
					LogDebug("%s parse json access_token %s expiretime %d", __func__, sAccessTokenOut.c_str(), iExpireTimeOut );
					ret = 0;
				}
			}
		}
	}
	else
	{
		LogErr("%s url %s fail, status %d", __func__, url, httpClient.Status() );
	}
	return ret;
}
	
int WeixinApi :: SendTxt( const string & sAccessToken, const string & sContent, const vector<string> & toUserList, bool isNotify, const string & sNotifyContent, string & sStatusOut )
{
	string json="";
	
	if ( toUserList.size() == 0 )
		return 0;

	//create msg
	unsigned long long msgid=0;
	{
		char url[1024]={0};
		snprintf( url, sizeof(url), "http://api.weixin.qq.com/messages?access_token=%s", sAccessToken.c_str() );

		webapp::HttpClient httpClient(url, "POST");
		
		vector< pair<string, string> > fieldsList;
		fieldsList.push_back( make_pair("type", "text") );
		fieldsList.push_back( make_pair("content", sContent) );
		httpClient.SetFields( fieldsList );
		int ret = httpClient.Execute();
		
		if ( 0 != ret )
		{
			LogErr("%s request %s fail, status %d", __func__, url, httpClient.Status() );
			return -3;
		}
		else
		{
			LogDebug("\n%s request %s ok, status %d\n", __func__, url, httpClient.Status() );
			LogDebug("size %d %s", httpClient.GetContent().size(), httpClient.GetContent().c_str() );
			
			Json::Value root;
			Json::Reader reader;
		
			if ( reader.parse( httpClient.GetContent(), root ) )
			{
				if ( root.isMember("message_id") )
				{
					msgid = root["message_id"].asUInt64();
					LogDebug("Parse msgid %lld", msgid );
				}
			}

			if ( msgid == 0 )
			{
				LogErr("%s CreateMsg Parse Msg fail", __func__ );
				return -4;
			}
		}
	}

	//sendmsg
	return SendMsg( sAccessToken, msgid, toUserList, sNotifyContent, sStatusOut );
}

int WeixinApi :: SendImg( const string & sAccessToken, const string & sFilePath, const vector<string> & toUserList, bool isNotify, const string & sNotifyContent, string & sStatusOut )
{
	int ret = -1;
	if ( toUserList.size() == 0 )
		return 0;
	
	//post media
	string sMediaId;
	if ( 0 != (ret = PostMedia( sAccessToken, "image", sFilePath, "test.jpg", sMediaId ) ) )
	{
		LogErr("%s accesstoken %s postmedia fail, ret %d", __func__, sAccessToken.c_str(), ret );
		return ret;
	}

	assert( sMediaId.size() > 0 );

	//create msg
	unsigned long long msgid=0;
	{
		char url[1024]={0};
		snprintf( url, sizeof(url), "http://api.weixin.qq.com/messages?access_token=%s", sAccessToken.c_str() );

		webapp::HttpClient httpClient(url, "POST");
		
		vector< pair<string, string> > fieldsList;
		fieldsList.push_back( make_pair("type", "image") );
		fieldsList.push_back( make_pair("media_id", sMediaId ) );
		httpClient.SetFields( fieldsList );
		int ret = httpClient.Execute();
		
		if ( 0 != ret )
		{
			LogErr("%s request %s fail, status %d", __func__, url, httpClient.Status() );
			return -3;
		}
		else
		{
			LogDebug("\n%s request %s ok, status %d\n", __func__, url, httpClient.Status() );
			LogDebug("size %d %s", httpClient.GetContent().size(), httpClient.GetContent().c_str() );
			
			Json::Value root;
			Json::Reader reader;
		
			if ( reader.parse( httpClient.GetContent(), root ) )
			{
				if ( root.isMember("message_id") )
				{
					msgid = root["message_id"].asUInt64();
					LogDebug("Parse msgid %lld", msgid );
				}
			}

			if ( msgid == 0 )
			{
				LogErr("%s CreateMsg Parse Msg fail", __func__ );
				return -4;
			}
		}
	}

	//send
	return SendMsg( sAccessToken, msgid, toUserList, sNotifyContent, sStatusOut );
}
	
int WeixinApi :: PostMedia( const string & sAccessToken, const string & sFileType, const string & sFilePath, const string & sFileName, string & sMediaId )
{
	char url[1024]={0};
	snprintf( url, sizeof(url), "http://api.weixin.qq.com/media?access_token=%s", sAccessToken.c_str() );

	webapp::HttpClient httpClient(url, "POST", true );

	httpClient.FormAdd( "type", sFileType );
	httpClient.FormAddBinary( "media", sFilePath, sFileName );
	httpClient.SetFormAsPostData();

	int ret = httpClient.Execute();

	if ( 0 != ret )
	{
		LogErr("%s request %s fail, ret %d status %d", __func__, url, httpClient.Status() );	
	}
	else
	{
		LogDebug("%s content %s", __func__, httpClient.GetContent().c_str() );
		//parse mediaid
		Json::Value root;
		Json::Reader reader;

		if ( reader.parse( httpClient.GetContent(), root ) )
		{
			if ( root.isMember("media_id") && root.isMember("type"))
			{
				sMediaId = root["media_id"].asCString();
				LogDebug("%s parse json media_id %s type %s", __func__, sMediaId.c_str(), root["type"].asCString() );
				ret = 0;
			}
		}
	}

	return ret;
}
	
int WeixinApi :: SendMsg( const string & sAccessToken, unsigned long long msgid, const vector<string> & toUserList, const string & sNotifyContent, string & sStatusOut )
{
	int ret = -1;
	string sToUsers = toUserList[0];
	for ( size_t i=1; i<toUserList.size(); ++i )
	{
		sToUsers += "," + toUserList[i];
	}
	
	char url[2048]={0};
	snprintf( url, sizeof(url), "http://api.weixin.qq.com/messages/send/%llu", msgid );

	webapp::HttpClient httpClient( url, "POST" );
	
	vector< pair<string, string> > fieldsList;
	fieldsList.push_back( make_pair("to_users", sToUsers.c_str()) );
	fieldsList.push_back( make_pair("access_token", sAccessToken.c_str()) );
	
	if ( sNotifyContent.size() > 0 )
	{
		fieldsList.push_back( make_pair("notify", "1" ) );
		fieldsList.push_back( make_pair("notify_str", sNotifyContent.c_str()) );
	}

	httpClient.SetFields( fieldsList );
	ret = httpClient.Execute();

	if ( 0 != ret )
	{
		LogErr("%s sendmsg request %s fail ret %d", __func__, url, ret );

		return -5;
	}

	LogDebug("%s request %s ok, status %d content %s", __func__, url, httpClient.Status(), httpClient.GetContent().c_str() );

	if ( httpClient.Status() == 200 )
	{
		Json::Reader reader;
		Json::Value root;

		if ( reader.parse( httpClient.GetContent(), root ) )
		{
			if ( root.isMember("status") )
			{
				sStatusOut = root["status"].asString();
				LogDebug("%s parse status %s ok", __func__, sStatusOut.c_str() );
			}
			ret = 0;
		}
	}

	return ret;
}
